package com.os.sys.common.auth;

import com.os.sys.common.utility.*;
import com.os.sys.model.ILoginUser;
import com.os.sys.model.T_SYS_User;
import com.os.sys.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class CommonAuthService {
    private final String g_salt = "myloginSalt"; // 加密盐
    private final String g_securityKey = "bingo22"; // 加密密钥
    private final String g_loginUserSessionKey = "loginUser"; // sessionid
    private final String g_tokenHasChk = "chk_";
    private final String g_tokenHasSessionCacheKey = "_hasSession";
    private final String g_decryptToken = "decrypt_";

    @Resource
    private HttpSession session;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PubClass pubClass;

    public void setCurrentUser(ILoginUser info) {
        List<String> list = new ArrayList<>();
        list.add(String.valueOf(info.getLoginUserID()));
        list.add(info.getLoginUserType());
        list.add(DateClass.getCurDate());
        list.add(g_salt);

        String token = SecurityClass.encryptAES(StringClass.join(list, "_"), g_securityKey);
        info.setLoginUserToken(token);

        // 获得用户有那些角色权限
        //REVIEW 这里可能需要根据登录用户类型，确定id是否是long还是string类型

        setLoginUserRoles(info);
//        setLoginUserAuthorities(info);
        session.invalidate(); // 使之前的匿名session失效 固定SessionID的漏洞（详见《黑客攻防技术宝典-web实战》第七章）
        session.setMaxInactiveInterval(28800);
        session.setAttribute(g_loginUserSessionKey, token);

//        var json=JsonClass.toJSON(info.getLoginUserGrantedAuthority());
//      var listp=  JSON.parseArray(json,GrantedAuthority.class);
//        List<SimpleGrantedAuthority> listp= JsonClass.fromJSON(json,new TypeReference<List<SimpleGrantedAuthority>>(){});
        pubClass.setCache(token, info, 60 * 8);


    }

//    private void setLoginUserAuthorities(ILoginUser userInfo) {
//        Set<SimpleGrantedAuthority> authList = new HashSet<SimpleGrantedAuthority>();
//        if (userInfo.getLoginUserRolesKey() != null) {
//            userInfo.getLoginUserRolesKey().forEach(q -> authList.add(new SimpleGrantedAuthority(q)));
//        }
//
//        if (userInfo.getLoginUserRolesPower() != null) {
//            userInfo.getLoginUserRolesPower().forEach(q -> authList.add(new SimpleGrantedAuthority(q)));
//        }
//
//        userInfo.setLoginUserGrantedAuthority(authList);
//    }

    public void setLoginUserRoles(ILoginUser info) {

        // 获得用户配置好的权限
        var listRolesKey = roleService.getRolesKeysByUserId(info.getLoginUserID());
        var listRolesPower = roleService.getRolesPowerListByUserId(info.getLoginUserID());


        // 附加免鉴权角色
        listRolesKey.add("COMMONT_ROLE");
        List<String> rolePower = roleService.getRolePowerByRoleKey("COMMONT_ROLE");
        if (rolePower != null) {
            listRolesPower.addAll(rolePower);
        }

        info.setLoginUserRolesKey(StringClass.join(listRolesKey, ","));
        info.setLoginUserRolesPower(StringClass.join(listRolesPower.stream().distinct().collect(Collectors.toList()), ","));


    }

    // 验证token真实性，为了安全性，api授权的token加密方式可以考虑和user授权方式不一致
    public boolean chkToken(String token) {
        try {
            String[] tokenInfo = token.split("_");
            String m_userId = tokenInfo[0];
            String m_userType = tokenInfo[1];
            Date m_createTime = DateClass.parseDate(tokenInfo[2], "yyyyMMddHHmmss");
            String m_salt = tokenInfo[3];

            // 验证加密盐
            if (!Objects.equals(m_salt, g_salt)) {
                return false;
            }

            //REVIEW 这里还可以加入验证m_createTime是否超过最大时间，但是目前用了redis的自动失效来控制，所以暂时不加入验证逻辑

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public XJsonInfo loginOut() {
        String token = getToken();
        if (token != null) {
            session.removeAttribute(g_loginUserSessionKey);
            pubClass.removeCache(token);
            pubClass.removeCache(g_tokenHasChk + token);
        }

        return new XJsonInfo();
    }

    public void updateCurrentUser(ILoginUser info) {
        pubClass.setCache(getToken(), info, 60 * 8);
    }

    //REVIEW 第三方授权的获得token的方式，应该考虑和本地系统的获得token的方式区分开来
    public String getToken() {
        HttpServletRequest request = WebClass.getRequest();

        String loginusertoken = request.getHeader("loginusertoken"); // 从ajax的header检查
        if (StringClass.isNullOrEmpty(loginusertoken)) {
            loginusertoken = WebClass.requestToString("token"); // 从网页url地址检查
            if (StringClass.isNullOrEmpty(loginusertoken)) {
                loginusertoken = (String) session.getAttribute(g_loginUserSessionKey); // 从session检查
                if (StringClass.isNullOrEmpty(loginusertoken)) {
                    return null;
                }
            }
        }

        return loginusertoken;
    }

    public boolean checkAPIAuth() {
        String apiToken = getToken();

        if (StringClass.isNullOrEmpty(apiToken)) {
            return false;
        }

        String token = decryptToken(apiToken);

        if (!chkToken(token)) {
            return false;
        }

        return true;
    }

    private void saveHasChkToken(String chkToken, boolean isOK) {
        pubClass.setCache(g_tokenHasChk + chkToken, isOK, 30);
    }

    public ILoginUser getLoginUser() {
        if (!chkLoginUser()) {
            return null;
        }
        String loginusertoken = getToken();
        if (StringClass.isBlank(loginusertoken)) {
            return null;
        }
        String tokenKey = pubClass.getCache(g_decryptToken + loginusertoken).toString();
        if (StringClass.isBlank(tokenKey)) {
            return null;
        }

        String[] tokenInfo = tokenKey.split("_"); //REVIEW 这里可以优化用类代替
        String m_userId = tokenInfo[0];
        String m_userType = tokenInfo[1];
        Date m_createTime = DateClass.parseDate(tokenInfo[2], "yyyyMMddHHmmss");
        String m_salt = tokenInfo[3];

        ILoginUser info = null;
        info = (T_SYS_User) pubClass.getCache(loginusertoken);

        return info;
    }

    public boolean chkLoginUser() {
        String loginusertoken = getToken();
        if (StringClass.isBlank(loginusertoken)) {
            return false;
        }

        // 为了减少验证过程，只要缓存中该键值是true，就表示已经验证过
        Object obj = pubClass.getCache(g_tokenHasChk + loginusertoken);
        if (obj != null && (boolean) obj) {
            return true;
        }
        // 验证session和token是否一样
        if (session != null) {
            if (pubClass.getCache(loginusertoken + g_tokenHasSessionCacheKey) != null) {
                if (!Objects.equals(session.getAttribute(g_loginUserSessionKey), loginusertoken)) {
                    return false;
                }
            }
        }

        if (!pubClass.hasCache(loginusertoken)) {
            saveHasChkToken(loginusertoken, false);
            return false;
        }

        String token = decryptToken(loginusertoken);
        if (StringClass.isNullOrEmpty(token)) {
            saveHasChkToken(loginusertoken, false);
            return false;
        }

        if (!chkToken(token)) {
            saveHasChkToken(loginusertoken, false);
            return false;
        } else {
            saveHasChkToken(loginusertoken, true);
            pubClass.setCache(g_decryptToken + loginusertoken, token, 60 * 8);
            return true;
        }
//        }
    }


    private String encryptToken(String str) {
        return SecurityClass.encryptAES(str, g_securityKey);
    }

    private String decryptToken(String str) {
        return SecurityClass.decryptAES(str, g_securityKey);
    }


}
